package com.csx.base.concurrency.sync;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

/**
 * <p> what does this class do ? </p>
 * <p> How does it work ?</p>
 * <p> Why does it do this ?</p>
 *
 * @author cuisongxu
 * @date 2024/11/30 周六 17:05
 */
public class TestSynchronized {

    private static int total = 0;

    private final Object lock = new Object();

    private static final Object consumerProducerLock = new Object();

    // 生产者生产出的产品数量
    private static int productNum = 0;

    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");


    // 方式一: 直接在 成员方法 上加锁,使用的是this实例锁
    private synchronized void syncIncrement() {
        total++;
    }

    // 方式二: 直接在 静态方法上 加锁,使用的是TestSynchronized.class 类锁,多个实例间生效
    private static synchronized void staticSyncIncrement() {
        total++;
    }

    // 方式三: 使用synchronized 代码块,代码块中使用自定义的object锁
    private void codeBlockSyncIncrement() {
        synchronized (lock) {
            total++;
        }
    }

    // 无锁
    private void noSyncIncrement() {
        total++;
    }

    public int testIncrement(Runnable task) {

        int res = 0;
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                task.run();
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                task.run();
            }
        });
        Thread thread3 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                task.run();
            }
        });

        // 启动线程
        thread1.start();
        thread2.start();
        thread3.start();

        try {
            thread1.join();
            thread2.join();
            thread3.join();
            res = total;
            // 将最终结果恢复,便于二次测试
            total = 0;
            return res;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 生产者-消费者模型 wait-notify的正确用法
     */
    static class Producer implements Runnable{

        public void produce() {
            synchronized (consumerProducerLock) {
                try {
                    // 生产者生产出的产品数量上限
                    int productNumLimit = 10;
                    while(productNum >= productNumLimit) {
                        LocalDateTime now = LocalDateTime.now();
                        String nowTime = now.format(formatter);
                        System.out.println(nowTime + " 产品数量为: " + productNum + " 等待消费者消费");
                        consumerProducerLock.wait();
                    }

                    productNum++;
                    LocalDateTime now = LocalDateTime.now();
                    String nowTime = now.format(formatter);
                    System.out.println(nowTime + " 生产者 [ " + Thread.currentThread().getName() + " ] 生产出产品,当前产品数量: "  + productNum);
                    consumerProducerLock.notify();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }


            }
        }

        @Override
        public void run() {
            while (!Thread.interrupted()) {
                try {
                    TimeUnit.MILLISECONDS.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                produce();
            }
        }
    }
    static class Consumer implements Runnable{

        @Override
        public void run() {
            while (!Thread.interrupted()) {
                try {
                    TimeUnit.MILLISECONDS.sleep(2000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                consume();
            }
        }

        public void consume() {
            synchronized (consumerProducerLock) {
                try {
                    while(productNum <= 0) {
                        LocalDateTime now = LocalDateTime.now();
                        String nowTime = now.format(formatter);
                        System.out.println(nowTime + " 消费者 [ " + Thread.currentThread().getName() + " ] 报告产品不足，等待生产者生产");
                        consumerProducerLock.wait();
                    }

                    productNum--;
                    LocalDateTime now = LocalDateTime.now();
                    String nowTime = now.format(formatter);
                    System.out.println(nowTime + " 消费者 [ " + Thread.currentThread().getName() + " ] 消费了产品,当前产品剩余数量: " + productNum);
                    consumerProducerLock.notify();
                }catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }


            }
        }
    }

    private static Object myLock = new Object();

    static class TestInterruptWait implements Runnable{

        public void testWaitInterrupt() {
            synchronized (myLock) {
                while (true) {
                    try {
                        myLock.wait();
                    } catch (InterruptedException e) {
                        System.out.println("被打断了");
                        throw new RuntimeException(e);
                    }
                }
            }
        }

        @Override
        public void run() {
            testWaitInterrupt();
        }
    }

    public static void main(String[] args) throws InterruptedException {

        // 1. 测试synchronized三种用法
//        TestSynchronized testSynchronized = new TestSynchronized();

//        int res = testSynchronized.testIncrement(testSynchronized::syncIncrement);
//        System.out.println("使用 【this 实例锁】 自增数据结果: " + res + " 是否达到预期: [ "+ (3000 == res ? "是" : "否") + "]");
//
//        res = testSynchronized.testIncrement(TestSynchronized::staticSyncIncrement);
//        System.out.println("使用 【class 类锁】 自增数据结果: " + res + " 是否达到预期: [ "+ (3000 == res ? "是" : "否") + "]");
//
//        res = testSynchronized.testIncrement(testSynchronized::codeBlockSyncIncrement);
//        System.out.println("使用 【code block 代码块自定义锁】 自增数据结果: " + res + " 是否达到预期: [ "+ (3000 == res ? "是" : "否") + "]");
//
//        res = testSynchronized.testIncrement(testSynchronized::noSyncIncrement);
//        System.out.println("使用 【无锁】 自增数据结果: " + res + " 是否达到预期: [ "+ (3000 == res ? "是" : "否") + "]");


        // 2. 生产者-消费者测试
        // 该测试主要验证wait-notify的正确用法
//        Producer producer = new Producer();
//        Consumer consumer1 = new Consumer();
//        Consumer consumer2 = new Consumer();
//        Thread producerThread = new Thread(producer, "生产者");
//        Thread consumerThread1 = new Thread(consumer1,"消费者1号");
//        Thread consumerThread2 = new Thread(consumer2,"消费者2号");
//
//        producerThread.start();
//        consumerThread1.start();
//        consumerThread2.start();
//
//        try {
//            TimeUnit.SECONDS.sleep(9999999);
//        } catch (InterruptedException e) {
//            throw new RuntimeException(e);
//        }
//

        // 3. 测试 处在wait等待的线程可以被打断的机制
        TestInterruptWait testInterruptWait = new TestInterruptWait();
        Thread thread = new Thread(testInterruptWait);

        thread.start();

        TimeUnit.SECONDS.sleep(5);
        // 打断在wait等待的线程
        thread.interrupt();
        TimeUnit.SECONDS.sleep(5);
        System.out.println("main end");

    }


}
